﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;

namespace MPViz
{
    public partial class MainForm : Form
    {
        private string MPVizVersion = "0.3.1";
        private string filename;
        List<MPClass> mpClasses;
        List<MPRel> mpRels;

        public MainForm()
        {
            InitializeComponent();
        }

        private void saveFile()
        {
            StreamWriter sw = new StreamWriter(filename, false, Encoding.Default);
            sw.Write(richTextBox1.Text);
            sw.Close();
            textBox1.ForeColor = Color.Black;
            textBox1.Text = "'" + filename + "' saved.";
        }

        private void saveAs()
        {
            if (saveFileDialog1.ShowDialog() == DialogResult.OK)
            {
                updateFilename(saveFileDialog1.FileName);
                saveFile();
            }
        }

        private void updateFilename(string name)
        {
            filename = name;
            this.Text = filename + " - MPViz " + MPVizVersion;
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            updateFilename("Noname");
        }

        private void newToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // Are you sure?
            if (MessageBox.Show("Are you sure?", "New file", MessageBoxButtons.YesNo, MessageBoxIcon.Warning)
                == DialogResult.Yes)
            {
                richTextBox1.Text = "";
                textBox1.ForeColor = Color.Black;
                textBox1.Text = "New file created.";
                updateFilename("Noname");
            }
            //richTextBox1.Focus();
        }

        private void openToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                StreamReader sr = new StreamReader(openFileDialog1.FileName, Encoding.Default);
                richTextBox1.Text = sr.ReadToEnd();
                sr.Close();
                textBox1.ForeColor = Color.Black;
                textBox1.Text = "'" + openFileDialog1.FileName + "' opened.";

                updateFilename(openFileDialog1.FileName);
            }
        }

        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (filename == "Noname") saveAs();
            else saveFile();
        }

        private void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            saveAs();
        }

        private void visualizeToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            if (!syntaxCheck()) return;
            StreamWriter outfile = new StreamWriter("temp.dot", false, Encoding.Default);

            // header
            outfile.WriteLine("digraph G {");
            outfile.WriteLine("node[fontname=\"Arial\" fontsize=20 shape=\"record\"]");
            outfile.WriteLine("edge[arrowsize=2.0 fontname=\"Arial\" fontsize=16 minlen=2]");
            outfile.WriteLine();

            //body
            foreach (MPClass mpclass in mpClasses) outfile.WriteLine(mpclass.toString4Dot());
            foreach (MPRel mprel in mpRels) outfile.WriteLine(mprel.toString4Dot());

            // tailer
            outfile.WriteLine();
            outfile.WriteLine("}");

            outfile.Close();

            System.Diagnostics.Process process = new System.Diagnostics.Process();
            string command = "dotty";
            string args = "temp.dot";
            process.EnableRaisingEvents = false;
            process.StartInfo.FileName = command;
            process.StartInfo.Arguments = args;
            process.Start();
        }

        private void mPWizardToolStripMenuItem_Click(object sender, EventArgs e)
        {
            WizardForm wf = new WizardForm();
            if (wf.ShowDialog() == DialogResult.OK)
            {
                richTextBox1.Text = wf.retStr;
            }

        }

        private bool hasRel(string c1, string c2, List<MPRel> mprels)
        {
            return true;
        }

        private string name2String(List<MPClass> mc)
        {
            string result = "";
            foreach (MPClass mpclass in mc)
            {
                if (result == "") result = mpclass.name;
                else result += ",\\n" + mpclass.name;
            }
            return result;
        }

        private string attr2String(List<MPClass> mc)
        {
            string result = "";
            foreach (MPClass mpclass in mc)
            {
                foreach(string a in mpclass.attr)
                    if (result == "") result = a;
                    else result += "," + a;
            }
            return result;
        }



        private void graphConvToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (!syntaxCheck()) return;
            StreamWriter outfile = new StreamWriter("temp.dot", false, Encoding.Default);

            // header
            outfile.WriteLine("digraph G {");
            outfile.WriteLine("node[fontname=\"Arial\" fontsize=20]");
            outfile.WriteLine("edge[arrowsize=2.0 fontname=\"Arial\" fontsize=16 minlen=2]");
            outfile.WriteLine();

            //body
            List<MPClass> mpverb, mpnoun, noun, lv1, lv2, lv3;
            mpverb = new List<MPClass>();
            mpnoun = new List<MPClass>();
            noun = new List<MPClass>();
            lv1 = new List<MPClass>();
            lv2 = new List<MPClass>();
            lv3 = new List<MPClass>();

            foreach (MPClass mpclass in mpClasses)
            {
                if (mpclass.type == "mp") mpnoun.Add(mpclass);
                //else if (mpclass.type == "verb" && (mpclass.name.Length < 5 || mpclass.name.Substring(0,5).ToLower() != "apply")) mpverb.Add(mpclass);
                else if (mpclass.type == "verb" && (mpclass.name.Length < 5 || mpclass.name.ToLower() != "realize")) mpverb.Add(mpclass);
            }

            string verbModLabel = "";
            string verbModType = "";
            string lv1ModLabel = "";
            string lv1ModType = "";
            string lv2ModLabel = "";
            string lv2ModType = "";
            string lv3ModLabel = "";
            string lv3ModType = "";

            foreach (MPClass mpclass in mpClasses)
            {
                if (mpclass.type == "noun")
                {
                    foreach (MPRel mprel in mpRels)
                    {
                        if (mprel.from == mpverb[0].number && mprel.to == mpclass.number)
                        {
                            mpclass.mod = "verb";
                            verbModLabel = mprel.label;
                            verbModType = mprel.type;
                            noun.Add(mpclass);
                            break;
                        }
                    }
                }
            }

            foreach (MPClass mpclass in mpClasses)
            {
                if (mpclass.type == "noun" && mpclass.mod == "")
                {
                    foreach (MPRel mprel in mpRels)
                    {
                        if (mprel.from == mpnoun[0].number && mprel.to == mpclass.number)
                        {
                            mpclass.mod = "lv1";
                            if (lv1ModLabel == "")
                                lv1ModLabel = mprel.label;
                            else
                                if (lv1ModLabel != mprel.label)
                                {
                                    lv1ModLabel += "," + mprel.label;
                                }
                            lv1ModType = mprel.type;
                            lv1.Add(mpclass);
                            break;
                        }
                    }
                }
            }
            foreach (MPClass mpclass in mpClasses)
            {
                if (mpclass.type == "noun" && mpclass.mod == "")
                {
                    foreach (MPRel mprel in mpRels)
                    {
                        if (mprel.from == lv1[0].number && mprel.to == mpclass.number)
                        {
                            mpclass.mod = "lv2";
                            if (lv2ModLabel == "")
                                lv2ModLabel = mprel.label;
                            else
                                if (lv2ModLabel != mprel.label)
                                {
                                    lv2ModLabel += "," + mprel.label;
                                }
                            lv2ModType = mprel.type;
                            lv2.Add(mpclass);
                            break;
                        }
                    }
                }
            }

            foreach (MPClass mpclass in mpClasses)
            {
                if (mpclass.type == "noun" && mpclass.mod == "")
                {
                    foreach (MPRel mprel in mpRels)
                    {
                        if (mprel.from == lv2[0].number && mprel.to == mpclass.number)
                        {
                            mpclass.mod = "lv3";
                            if (lv3ModLabel == "")
                                lv3ModLabel = mprel.label;
                            else
                                if (lv3ModLabel != mprel.label)
                                {
                                    lv3ModLabel += "," + mprel.label;
                                }
                            lv3ModType = mprel.type;
                            lv3.Add(mpclass);
                            break;
                        }
                    }
                }
            }

            outfile.WriteLine("0[label=\"{0}\"]", name2String(mpverb));
            outfile.WriteLine("1[label=\"Verb\"]");
            outfile.WriteLine("0->1[label=\"{0}\"]", "type");

            string at;

            if (noun.Count() > 0)
            {
                outfile.WriteLine("2[label=\"{0}\"]", name2String(noun));
                outfile.WriteLine("0->2[label=\"{0}\"]", "depend");
                //outfile.WriteLine("3[label=\"noun\"]");
                //outfile.WriteLine("2->3[label=\"{0}\"]", "type");
                at = attr2String(noun);
                if (at != "")
                {
                    outfile.WriteLine("4[label=\"{0}\"]", at);
                    outfile.WriteLine("2->4[label=\"{0}\"]", "attribute");
                }

            }
            outfile.WriteLine();

            outfile.WriteLine("10[label=\"{0}\"]", name2String(mpnoun));
            outfile.WriteLine("11[label=\"MP\"]");
            outfile.WriteLine("10->11[label=\"{0}\"]", "type");

            at = attr2String(mpnoun);
            if (at != "")
            {
                outfile.WriteLine("12[label=\"{0}\"]", at);
                outfile.WriteLine("10->12[label=\"{0}\"]", "attribute");
            }

            if (lv1.Count() > 0)
            {
                outfile.WriteLine("20[label=\"{0}\"]", name2String(lv1));
                if (lv1ModLabel != "") lv1ModLabel = ":" + lv1ModLabel;
                outfile.WriteLine("10->20[label=\"{0}\"]", lv1ModType + lv1ModLabel);

                at = attr2String(lv1);
                if (at != "")
                {
                    outfile.WriteLine("21[label=\"{0}\"]", at);
                    outfile.WriteLine("20->21[label=\"{0}\"]", "attribute");
                }
            }

            if (lv2.Count() > 0)
            {
                outfile.WriteLine("30[label=\"{0}\"]", name2String(lv2));
                if (lv2ModLabel != "") lv2ModLabel = ":" + lv2ModLabel;
                outfile.WriteLine("20->30[label=\"{0}\"]", lv2ModType + lv2ModLabel);

                at = attr2String(lv2);
                if (at != "")
                {
                    outfile.WriteLine("31[label=\"{0}\"]", at);
                    outfile.WriteLine("30->31[label=\"{0}\"]", "attribute");
                }
            }

            if (lv3.Count() > 0)
            {
                outfile.WriteLine("40[label=\"{0}\"]", name2String(lv3));
                if (lv3ModLabel != "") lv3ModLabel = ":" + lv3ModLabel;
                outfile.WriteLine("30->40[label=\"{0}\"]", lv3ModType + lv3ModLabel);

                at = attr2String(lv3);
                if (at != "")
                {
                    outfile.WriteLine("41[label=\"{0}\"]", at);
                    outfile.WriteLine("40->41[label=\"{0}\"]", "attribute");
                }
            }

            // tailer
            outfile.WriteLine();
            outfile.WriteLine("}");

            outfile.Close();

            System.Diagnostics.Process process = new System.Diagnostics.Process();
            string command = "dotty";
            string args = "temp.dot";
            process.EnableRaisingEvents = false;
            process.StartInfo.FileName = command;
            process.StartInfo.Arguments = args;
            process.Start();
        }

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            MessageBox.Show("MPViz is a toolset for Materialization Pattern(MP) models.", "About MPViz " + MPVizVersion);
        }

        private string parsing(string text)
        {
            List<MPClass> mpClasses = new List<MPClass>();
            List<MPRel> mpRels = new List<MPRel>();

            string[] lines = text.Split('\n');
            System.Collections.ArrayList classNumber = new System.Collections.ArrayList();
            
            for (int i = 0; i < lines.Length; i++)
            {
                StringBuilder sb = new StringBuilder(lines[i]);

                bool flag = false;

                for (int j = 0; j < sb.Length; j++)
                    if (sb[j] == '\"')
                    {
                        flag = !flag;
                        sb[j] = ' ';
                    }
                    else if (flag && sb[j] == ' ') sb[j] = '_';

                string ts = sb.ToString().Trim();
                while (ts != ts.Replace("  ", " "))
                    ts = ts.Replace("  ", " ");
                string[] tokens = ts.Split('\t', ' ');
                
                if (tokens.Length == 1 && tokens[0] == "") continue;

                if (tokens[0].ToLower() == "class")
                {
                    MPClass mpclass = new MPClass();

                    if (tokens.Length < 4)
                    {
                        return String.Format("line {0}: wrong class declaration.", i+1);
                    }

                    if (classNumber.IndexOf(tokens[1]) >= 0)
                        return String.Format("line {0}: duplicated class number '{1}'.", i + 1, tokens[1]);
                    classNumber.Add(tokens[1]);

                    mpclass.name = tokens[3].Replace('_', ' ');
                    mpclass.number = tokens[1];

                    switch (tokens[2].ToLower())
                    {
                        case "mp":
                        case "verb":
                        case "noun":
                            mpclass.type = tokens[2].ToLower();
                            break;
                        default:
                            return String.Format("line {0}: unknown class type '{1}'.", i + 1, tokens[2]);
                    }
                    if (tokens.Length > 4)
                    {
                        for (int j = 4; j < tokens.Length; j++)
                        {
                            mpclass.attr.Add(tokens[j].Replace('_', ' '));
                        }
                    }
                    mpClasses.Add(mpclass);
                }
                else if (tokens[0].ToLower() == "rel")
                {
                    MPRel mprel = new MPRel();

                    if (tokens.Length < 4)
                    {
                        return String.Format("line {0}: wrong relationship declaration.", i+1);
                    }

                    if (classNumber.IndexOf(tokens[2]) == -1)
                        return String.Format("line {0}: undeclared class number '{1}'.", i + 1, tokens[2]);
                    if (classNumber.IndexOf(tokens[3]) == -1)
                        return String.Format("line {0}: undeclared class number '{1}'.", i + 1, tokens[3]);

                    mprel.from = tokens[2];
                    mprel.to = tokens[3];

                    if (tokens.Length == 5) mprel.label = tokens[4].Replace('_', ' ');
                    if (tokens.Length > 5)
                    {
                        return String.Format("line {0}: a relation cannot have two or more labels.", i + 1);
                    }

                    switch (tokens[1].ToLower())
                    {
                        case "aggre":
                        case "general":
                        case "real":
                        case "depend":
                        case "prep":
                        case "assoc":
                        case "tran":
                            mprel.type = tokens[1].ToLower();
                            break;
                        default:
                            return String.Format("line {0}: unknown relationship type '{1}'.", i + 1, tokens[1]);
                    }
                    mpRels.Add(mprel);
                }
                else
                {
                    return String.Format("line {0}: unknown keyword '{1}'.", i+1, tokens[0]);
                }
            }

            this.mpClasses = mpClasses;
            this.mpRels = mpRels;
            return "";
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (MessageBox.Show("Are you sure?", "Exit MPViz "+MPVizVersion, MessageBoxButtons.YesNo, MessageBoxIcon.Warning)
                == DialogResult.Yes)
            {
                Close();
            }
        }

        private bool syntaxCheck()
        {
            string errMsg = parsing(richTextBox1.Text);
            if (errMsg == "")
            {
                textBox1.ForeColor = Color.Blue;
                textBox1.Text = "No error found.";
                return true;
            }
            else
            {
                textBox1.ForeColor = Color.Red;
                textBox1.Text = String.Format("Error: {0}", errMsg);
                return false;
            }
        }


        private void syntaxCheckToolStripMenuItem_Click(object sender, EventArgs e)
        {
            syntaxCheck();
        }
    }
}
